﻿#region << 文 件 说 明 >>
/*----------------------------------------------------------------
// 文件名称：TinyIOC
// 创 建 人：YangCheng
// 创建时间：2022/5/24 19:07:22
// 文件版本：V1.0.0
// ===============================================================
// 功能描述：
//		
//
//----------------------------------------------------------------*/
#endregion

using System;
using System.Collections.Generic;
using System.Formats.Asn1;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Vampirewal.Core.VContainer
{
    #region 注册进IOC特性
    /// <summary>
    /// 注册IOC容器
    /// </summary>
    [AttributeUsage(AttributeTargets.Class,AllowMultiple =false)]
    public class VIocRegisterAttribute : Attribute
    {
        /// <summary>
        /// 使用typeof(接口)
        /// </summary>
        public Type ServiceType { get; set; }

        /// <summary>
        /// 注入类型
        /// </summary>
        public RegisterType Way { get; set; }

        /// <summary>
        /// 是否注册单例模式(仅Way为OnlyClass有效)
        /// </summary>
        public bool IsSingleton { get; set; }


        /// <summary>
        /// 注册IOC容器
        /// </summary>
        /// <param name="way">方式</param>
        /// <param name="isSingleton">是否注册单例模式</param>
        public VIocRegisterAttribute(RegisterType way, bool isSingleton = false)
        {
            //构造函数
            Way = way;
            IsSingleton = isSingleton;
        }
    }

    /// <summary>
    /// 方式
    /// </summary>
    public enum RegisterType
    {
        /// <summary>
        /// 仅类
        /// </summary>
        OnlyClass,
        /// <summary>
        /// 服务
        /// </summary>
        Service
    }
    #endregion

    #region 通过IOC获取实例特性
    [AttributeUsage(AttributeTargets.Property)]
    public class VIoCGetInstanceAttribute : Attribute
    {
        public string ClassName { get; set; }

        public Type type { get; set; }

        /// <summary>
        /// 注入类型
        /// </summary>
        public RegisterType Way { get; set; }

        public VIoCGetInstanceAttribute(RegisterType Way, string className = "")
        {
            ClassName = className;
            this.Way = Way;
        }
    }


    #endregion

    /// <summary>
    /// Vampirewal.IoC容器
    /// <para>1、支持特性注册，<paramref name="VIocRegisterAttribute"/>标记在类上，初始化程序时，调用VIoC.GetInstance().RegisterAll();即可</para>
    /// <para>2、支持类名注入，可进一步解耦,VIoC.GetInstance().GetInstance("Test3");</para>
    /// <para>3、支持属性注入，<paramref name="VIoCGetInstanceAttribute"/>标记在属性上即可，同时也支持传入类名(<paramref name="必须和类的名称一致"/>)</para>
    /// </summary>
    public class VIoC : BaseSingleton<VIoC>
    {
        Dictionary<Type, Type> RegisteredServices { get; set; } = new Dictionary<Type, Type>();
        Dictionary<Type, object> instances { get; set; } = new Dictionary<Type, object>();

        /// <summary>
        /// 注册所有标记了特性<typeparamref name="VIocRegisterAttribute"/>的类
        /// </summary>
        public void RegisterAll()
        {
            /*
             * 注意事项：
             *     1、如果是WPF使用的话，建议把ViewModelLocator放在VM类库中
             *     2、然后再调用这个，才能在初始化的时候，找到VM这个类库，然后加载里面的ViewModel，不然的话，找不到VM这个程序集，会无法加载ViewModel
             */

            var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Where(w => w.GetCustomAttribute<VIocRegisterAttribute>() != null).ToArray();

            foreach (var v in types)
            {
                var Attribute = v.GetCustomAttribute<VIocRegisterAttribute>();

                if (Attribute != null)
                {
                    if (Attribute.Way == RegisterType.OnlyClass)
                    {
                        if (Attribute.IsSingleton)
                        {
                            ConstructorInfo[] infoCollection = GatherConstructors(v);
                            this.instances.Add(v, ConstructService(v, infoCollection));
                        }
                        else
                        {
                            this.RegisteredServices.Add(v, v);
                        }
                    }
                    else if (Attribute.Way == RegisterType.Service)
                    {
                        this.RegisteredServices.Add(Attribute.ServiceType, v);
                    }
                }

            }
        }


        #region 注册
        /// <summary>
        /// 注册
        /// </summary>
        /// <typeparam name="TInterface">接口</typeparam>
        /// <typeparam name="TClass">实现</typeparam>
        public void Register<TInterface, TClass>() where TInterface : class where TClass : class
        {
            if (!this.RegisteredServices.ContainsKey(typeof(TInterface)))
                this.RegisteredServices.Add(typeof(TInterface), typeof(TClass));
            else
                throw new ArgumentException($"重复的Key值：{typeof(TInterface).Name}");
        }

        /// <summary>
        /// 注册
        /// </summary>
        /// <typeparam name="TInterface">接口</typeparam>
        /// <typeparam name="TClass">实现</typeparam>
        public void RegisterSingleton<TInterface, TClass>() where TInterface : class where TClass : class
        {
            if (!this.instances.ContainsKey(typeof(TInterface)))
            {
                ConstructorInfo[] infoCollection = GatherConstructors(typeof(TClass));
                this.instances.Add(typeof(TInterface), ConstructService(typeof(TClass), infoCollection));
            }
                
            else
                throw new ArgumentException($"重复的Key值：{typeof(TInterface).Name}");
        }

        /// <summary>
        /// 注册
        /// </summary>
        /// <typeparam name="TClass"></typeparam>
        public void Register<TClass>()
        {
            if (!this.RegisteredServices.ContainsKey(typeof(TClass)))
                this.RegisteredServices.Add(typeof(TClass), typeof(TClass));
            else
                throw new ArgumentException($"重复的Key值：{typeof(TClass).Name}");
        }

        /// <summary>
        /// 注册
        /// </summary>
        /// <typeparam name="TClass"></typeparam>
        /// <param name="class"></param>
        /// <exception cref="ArgumentException"></exception>
        public void Register<TClass>(TClass @class)
        {
            if (!this.RegisteredServices.ContainsKey(typeof(TClass)))
                this.RegisteredServices.Add(typeof(TClass), typeof(TClass));
            else
                throw new ArgumentException($"重复的Key值：{typeof(TClass).Name}");
        }

        /// <summary>
        /// 注册单例模式
        /// </summary>
        /// <typeparam name="TClass"></typeparam>
        public void RegisterSingleton<TClass>()
        {
            if (!this.instances.ContainsKey(typeof(TClass)))
            {
                ConstructorInfo[] infoCollection = GatherConstructors(typeof(TClass));
                this.instances.Add(typeof(TClass), ConstructService(typeof(TClass), infoCollection));
            }


            else
                throw new ArgumentException($"重复的Key值：{typeof(TClass).Name}");
        }

        /// <summary>
        /// 注册单例模式
        /// </summary>
        /// <typeparam name="TClass"></typeparam>
        /// <param name="instance"></param>
        public void RegisterSingleton<TClass>(TClass instance) where TClass : class
        {
            if (!this.instances.ContainsKey(typeof(TClass)))
                this.instances.Add(typeof(TClass), instance);
            else
                throw new ArgumentException($"重复的Key值：{typeof(TClass).Name}");
        }
        #endregion

        #region 获取实例
        /// <summary>
        /// 获取实例
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <returns></returns>
        public T GetInstance<T>() where T : class
        {
            return (T)this.GetInstance(typeof(T));
        }

        /// <summary>
        /// 获取实例
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public object GetInstance(Type type)
        {

            if (instances.TryGetValue(type, out object value))
            {
                return value;
            }

            if (RegisteredServices.TryGetValue(type, out Type implementation))
            {
                ConstructorInfo[] infoCollection = GatherConstructors(implementation);
                return ConstructService(implementation, infoCollection);
            }
            throw new ArgumentException($"没有注册实现给定类型的服务: {type.ToString()}");
        }

        /// <summary>
        /// 通过类名获取实例
        /// </summary>
        /// <param name="ClassName"></param>
        /// <returns></returns>
        public object GetInstance(string ClassName)
        {
            Type type = null;

            type = instances.Keys.FirstOrDefault(f => f.Name == ClassName);

            if (type == null)
            {
                type = RegisteredServices.Keys.FirstOrDefault(f => f.Name == ClassName);
            }

            if (type != null)
            {
                return GetInstance(type);
            }

            return type;
        }

        /// <summary>
        /// 指定返回类型和类名获取实例
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ClassName"></param>
        /// <returns></returns>
        public T GetInstance<T>(string ClassName)
        {
            return (T)this.GetInstance(ClassName);
        }
        #endregion

        #region 内部方法
        /// <summary>
        /// 处理注入
        /// </summary>
        /// <param name="implementation"></param>
        /// <param name="infoCollection"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        private object ConstructService(Type implementation, ConstructorInfo[] infoCollection)
        {
            for (int i = 0; i < infoCollection.Length; i++)
            {
                ConstructorInfo constructorInfo = infoCollection[i];
                ParameterInfo[] parameters = constructorInfo.GetParameters();

                if (parameters.Length == 0)
                {
                    var obj = Activator.CreateInstance(implementation);

                    CheckPropertyDI(implementation, obj);
                    return obj;
                }

                try
                {
                    List<object> paramInstances = ResolveParameters(parameters);

                    var obj = Activator.CreateInstance(implementation, paramInstances.ToArray());

                    CheckPropertyDI(implementation, obj);
                    return obj;
                }
                catch
                {
                    if (i < infoCollection.Length - 1)
                    {
                        continue;
                    }

                    throw;
                }
            }

            throw new ArgumentException($"无法解决服务问题 {implementation.FullName}");
        }

        /// <summary>
        /// 获取构造函数及构造函数的数量
        /// </summary>
        /// <param name="implementation"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        private static ConstructorInfo[] GatherConstructors(Type implementation)
        {
            ConstructorInfo[] infoCollection = implementation.GetConstructors();

            if (infoCollection.Length <= 0)
            {
                throw new ArgumentException($"在这个类型上没有找到公共构造函数: {implementation.FullName}");
            }

            return infoCollection;
        }

        /// <summary>
        /// 获取这个构造函数里的参数并实例化出来
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        private List<object> ResolveParameters(ParameterInfo[] parameters)
        {
            var paramInstances = new List<object>();
            foreach (var par in parameters)
            {
                var paramType = par.ParameterType;
                var instance = this.GetInstance(paramType);
                paramInstances.Add(instance);
            }

            return paramInstances;
        }

        /// <summary>
        /// 属性注入
        /// </summary>
        /// <param name="type"></param>
        /// <param name="obj"></param>
        private void CheckPropertyDI(Type type, object obj)
        {
            var Properties = type.GetProperties().Where(w => w.GetCustomAttribute<VIoCGetInstanceAttribute>() != null).ToArray();

            foreach (var item in Properties)
            {
                var Attribute = item.GetCustomAttribute<VIoCGetInstanceAttribute>();

                if (!string.IsNullOrEmpty(Attribute.ClassName))
                {
                    var entity = GetInstance(Attribute.ClassName);
                    item.SetValue(obj, entity);

                    continue;
                }

                if (Attribute.Way == RegisterType.OnlyClass)
                {
                    item.SetValue(obj, GetInstance(item.PropertyType.Name));
                }
                else
                {
                    Type cur;

                     cur = instances.Keys.FirstOrDefault(f => f.Name == item.PropertyType.Name);

                    if (cur == null)
                    {
                        cur = RegisteredServices.Keys.FirstOrDefault(f => f.Name == item.PropertyType.Name);
                    }

                    item.SetValue(obj, GetInstance(cur));
                }
            }
        }
        #endregion
    }
}
